#include <assert.h>
#include <iostream>

#include "foreach.hpp"
#include "gamemap.hpp"
#include "string_utils.hpp"
#include "terrain.hpp"
#include "tile_logic.hpp"

namespace hex {

namespace {
bool string_empty(const std::string& str) { return str.empty(); }
}

gamemap::gamemap(const std::string& str)
{
	std::vector<std::string> lines = util::split(str, '\n');
	lines.erase(std::remove_if(lines.begin(), lines.end(), string_empty), lines.end());
	height_ = lines.size();
	if(height_ == 0) {
		std::cerr << "map is empty!\n";
		throw parse_error();
	}

	width_ = util::split(lines.front(), ',').size();
	if(width_ == 0) {
		std::cerr << "map is empty!\n";
		throw parse_error();
	}
	
	foreach(const std::string& line, lines) {
		std::vector<std::string> cells = util::split(line, ',');
		if(cells.size() != width_) {
			std::cerr << "row does not have the right number of cells!\n";
			throw parse_error();
		}

		foreach(const std::string& cell, cells) {
			const_terrain_ptr t = terrain::get(cell);
			if(!t) {
				std::cerr << "unrecognized terrain: '" << cell << "'\n";
				throw parse_error();
			}

			const int index = tiles_.size();
			location loc(index%width_, index/width_);
			tiles_.push_back(tile_ptr(new tile(loc, t)));
		}
	}

	assert(tiles_.size() == width_*height_);

	for(int x = 0; x != width_; ++x) {
		for(int y = 0; y != height_; ++y) {
			location loc(x, y);
			location adj[6];
			const tile* adj_tiles[6];
			get_adjacent_tiles(loc, adj);
			for(int n = 0; n != 6; ++n) {
				adj_tiles[n] = get_tile(adj[n]).get();
			}

			tile_ptr t = get_tile(loc);
			assert(t);
			t->set_adjacent(adj_tiles);
		}
	}
}

void gamemap::draw() const
{
	foreach(const tile_ptr t, tiles_) {
		t->draw();
	}

	foreach(const tile_ptr t, tiles_) {
		t->draw_overlay();
	}
}

bool gamemap::is_loc_on_map(const location& loc) const
{
	return loc.x() >= 0 && loc.y() >= 0 && loc.x() < width_ && loc.y() < height_;
}

const_tile_ptr gamemap::get_tile(const location& loc) const
{
	if(!is_loc_on_map(loc)) {
		return const_tile_ptr();
	}

	return tiles_[loc.y()*width_ + loc.x()];
}

tile_ptr gamemap::get_tile(const location& loc)
{
	if(!is_loc_on_map(loc)) {
		return tile_ptr();
	}

	return tiles_[loc.y()*width_ + loc.x()];
}

}
